package it.gma.torero.parsing;

import it.gma.torero.parsing.Tokenizer.MalformedException;
import it.gma.torero.parsing.token.And;
import it.gma.torero.parsing.token.BinaryComparatorOperator;
import it.gma.torero.parsing.token.BynaryOperator;
import it.gma.torero.parsing.token.Group;
import it.gma.torero.parsing.token.Or;
import it.gma.torero.parsing.token.Token;
import it.gma.torero.parsing.token.UnaryOperator;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class ExpressionTreeBuilder {

	public static Token getRoot(Tokenizer tokenizer_) throws MalformedException {
		List<Token> words = new ArrayList<Token>();
		while (tokenizer_.hasMoreElements()) {
			words.add(tokenizer_.nextElement());
		}

		if (words.size() == 1) {
			Token next = words.get(0);
			return unwrappedElement(next);
		}

		// Apply Not Priority
		words = applyNotPriority(words);
		// Apply comparation priority
		words = applyComparationPriority(words);
		// Apply and priority
		words = applyAndPriority(words);
		// Apply or
		words = applyOrPriority(words);

		if (words.size() > 1) {
			throw new MalformedException("Error in expression " + tokenizer_ + " missed operators between oprands " + words);
		}

		return unwrappedElement(words.get(0));
	}

	private static List<Token> applyNotPriority(List<Token> tokenizer) throws MalformedException {
		Iterator<Token> wordsIt = tokenizer.iterator();
		while (wordsIt.hasNext()) {
			Token curr = wordsIt.next();
			if (curr instanceof UnaryOperator) {
				if (wordsIt.hasNext()) {
					((UnaryOperator) curr).setRightOperand(unwrappedElement(wordsIt.next()));
					wordsIt.remove();
				} else {
					throw new MalformedException("Right operator missing for operator " + curr.getSequence());
				}
			}
		}
		return tokenizer;
	}

	private static List<Token> applyComparationPriority(List<Token> tokenizer) throws MalformedException {

		// ArrayList<int[]> toaggregate = new ArrayList<int[]>();
		for (int i = 0; i < tokenizer.size(); i++) {

			// if (tokenizer.size() == 1) {
			// break;
			// }

			Token current = tokenizer.get(i);
			if (current instanceof BinaryComparatorOperator && !((BinaryComparatorOperator) current).hasOperands()) {

				if (i + 1 >= tokenizer.size()) {
					throw new MalformedException("Right operator missing for operator " + current.getSequence());
				}
				if (i == 0) {
					throw new MalformedException("Right operator missing for operator " + current.getSequence());
				}

				Token right = tokenizer.get(i + 1);

				((BynaryOperator) current).setRightOperand(unwrappedElement(right));

				Token left = tokenizer.get(i - 1);

				((BynaryOperator) current).setLeftOperand(unwrappedElement(left));

				tokenizer.remove(left);
				tokenizer.remove(right);

				i = 0;
			}
		}

		return tokenizer;
	}

	private static List<Token> applyAndPriority(List<Token> tokenizer) throws MalformedException {

		// ArrayList<int[]> toaggregate = new ArrayList<int[]>();
		for (int i = 0; i < tokenizer.size(); i++) {

			// if (tokenizer.size() == 1) {
			// break;
			// }

			Token current = tokenizer.get(i);
			if (current instanceof And && !((And) current).hasOperands()) {
				if (i + 1 >= tokenizer.size()) {
					throw new MalformedException("Right operator missing for operator " + current.getSequence());
				}
				if (i == 0) {
					throw new MalformedException("Right operator missing for operator " + current.getSequence());
				}

				Token right = tokenizer.get(i + 1);

				((BynaryOperator) current).setRightOperand(unwrappedElement(right));

				Token left = tokenizer.get(i - 1);

				((BynaryOperator) current).setLeftOperand(unwrappedElement(left));

				tokenizer.remove(left);
				tokenizer.remove(right);

				i = 0;
			}
		}

		return tokenizer;
	}

	private static List<Token> applyOrPriority(List<Token> tokenizer) throws MalformedException {

		// ArrayList<int[]> toaggregate = new ArrayList<int[]>();
		for (int i = 0; i < tokenizer.size(); i++) {

			// if (tokenizer.size() == 1) {
			// break;
			// }

			Token current = tokenizer.get(i);
			if (current instanceof Or && !((Or) current).hasOperands()) {
				if (i + 1 >= tokenizer.size()) {
					throw new MalformedException("Right operator missing for operator " + current.getSequence());
				}
				if (i == 0) {
					throw new MalformedException("Right operator missing for operator " + current.getSequence());
				}

				Token right = tokenizer.get(i + 1);

				((BynaryOperator) current).setRightOperand(unwrappedElement(right));

				Token left = tokenizer.get(i - 1);

				((BynaryOperator) current).setLeftOperand(unwrappedElement(left));

				tokenizer.remove(left);
				tokenizer.remove(right);

				i = 0;
			}
		}

		return tokenizer;
	}

	private static Token unwrappedElement(Token elem) throws MalformedException {

		Token result = elem;

		while (result instanceof Group) {
			result = ((Group) result).getRoot();
		}

		return result;
	}

}
